package com.jweb.framework.ws.support;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;



import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.commons.schema.ValidationEventHandler;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaExternal;
import org.apache.ws.commons.schema.XmlSchemaImport;
import org.apache.ws.commons.schema.XmlSchemaInclude;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.apache.ws.commons.schema.resolver.DefaultURIResolver;
import org.apache.ws.commons.schema.resolver.URIResolver;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.UrlResource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.xml.sax.SaxUtils;
import org.springframework.xml.validation.XmlValidator;
import org.springframework.xml.validation.XmlValidatorFactory;
import org.springframework.xml.xsd.XsdSchema;
import org.springframework.xml.xsd.XsdSchemaCollection;
import org.springframework.xml.xsd.commons.CommonsXsdSchemaException;
import org.xml.sax.InputSource;
/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：.xsd 文件解析集合
 */
public class CommonsXsdSchemaCollectionJWeb implements InitializingBean , ResourceLoaderAware , XsdSchemaCollection {
	
private static final Log logger = LogFactory.getLog(CommonsXsdSchemaCollectionJWeb.class);
	
	private final XmlSchemaCollection schemaCollection = new XmlSchemaCollection();
	
	private final List<XmlSchema> xmlSchemas = new ArrayList<XmlSchema>();
	
	private Resource[] xsdResources ;
	
	private boolean inline = false ;
	
	private ValidationEventHandler validationEventHandler ;
	
	private URIResolver uriResolver = new ClasspathUriResolver();
	
	private ResourceLoader resourceLoader ;
	
	
	
	public CommonsXsdSchemaCollectionJWeb(){
		
	}
	
	public CommonsXsdSchemaCollectionJWeb(Resource[] resources){
		this.xsdResources = resources ; //[Inline schema for ServletContext resource [/WEB-INF/wsdl/server/temperature.wsdl]]
	}
	
	public void setXsds(Resource[] xsdResources){
		this.xsdResources = xsdResources ;
	}
	
	public void setInline(boolean inline) {
		this.inline = inline ;
	}
	
	public void setValidationEventHandler(ValidationEventHandler validationEventHandler){
		this.validationEventHandler = validationEventHandler ;
	}
	
	public void setUriResolver(URIResolver uriResolver){
		Assert.notNull(uriResolver, "'uriResolver' must not be null");
		this.uriResolver = uriResolver ;
	}
	
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader ;
	}
	
	public void afterPropertiesSet() throws IOException {
		
		Assert.notEmpty(xsdResources , "'xsds' must not be empty");
		
		schemaCollection.setSchemaResolver(uriResolver);
		
		Set<XmlSchema> processedIncludes = new HashSet<XmlSchema>();
		Set<XmlSchema> processedImports = new HashSet<XmlSchema>();
		
		for(Resource xsdResource : xsdResources){ // xsdResource={Inline schema for class path resource [wsdl/ibsclient/QueryAcctDateService.wsdl]}
			Assert.isTrue(xsdResource.exists() , xsdResource + " does not exit");
			try {   // read(inputSource,veh):Read an XML schema into the collection from a SAX InputSource.
				XmlSchema xmlSchema = schemaCollection.read(SaxUtils.createInputSource(xsdResource) , this.validationEventHandler);
				xmlSchemas.add(xmlSchema);
				
				if(inline){
					inlineIncludes(xmlSchema , processedIncludes , processedImports);
					findImports(xmlSchema, processedImports, processedIncludes); // 将temperature.wsdl,temperature.xsd,headerOfResponse.xsd放到集合xmlSchemas.
				}
				
			} catch (Exception e) {
				throw new CommonsXsdSchemaException("Schema [" + xsdResource + "] could not be loaded", e);
			}
		}
		
		if(logger.isInfoEnabled()){
			logger.info("Loaded" + StringUtils.arrayToCommaDelimitedString(xsdResources));
		}
	}
	
	public XsdSchema[] getXsdSchemas(){
		XsdSchema[] result =  new XsdSchema[xmlSchemas.size()]; // xmlSchemas=[org.apache.ws.commons.schema.XmlSchema@70617061[http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0], org.apache.ws.commons.schema.XmlSchema@114d114d[http://gsp.service.bankcomm.com/QueryAcctDateService], org.apache.ws.commons.schema.XmlSchema@3a6d3a6d[http://gsp.bankcomm.com]]
		for(int i = 0 ; i < xmlSchemas.size() ; i++){
			XmlSchema xmlSchema = xmlSchemas.get(i);
			result[i] = new CommonsXsdSchemaJWeb(xmlSchema , schemaCollection);
		}
		return result ;
	}
	
	public XmlSchema[] getSchemas(){
		return this.xmlSchemas.toArray(new XmlSchema[0]);
	}
	
	public XmlSchemaCollection getSchemasCollection(){
		return this.schemaCollection ; //org.apache.ws.commons.schema.XmlSchemaCollection@1c9a1c9a[{{http://www.w3.org/2001/XMLSchema}http://www.w3.org/2001/XMLSchema=org.apache.ws.commons.schema.XmlSchema@69bc69bc[http://www.w3.org/2001/XMLSchema], {http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0}file:/E:/spring/weather/WebRoot/WEB-INF/classes/wsdl/ibsclient/QueryAcctDateService.wsdl=org.apache.ws.commons.schema.XmlSchema@6efc6efc[http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0], {http://gsp.service.bankcomm.com/QueryAcctDateService}file:/E:/spring/weather/WebRoot/WEB-INF/classes/wsdl/ibsclient/QueryAcctDateService.xsd=org.apache.ws.commons.schema.XmlSchema@14c014c0[http://gsp.service.bankcomm.com/QueryAcctDateService], {http://gsp.bankcomm.com}file:/E:/spring/weather/WebRoot/WEB-INF/classes/headerOfResponse.xsd=org.apache.ws.commons.schema.XmlSchema@1e381e38[http://gsp.bankcomm.com]}]
	}
	
	public XmlValidator createValidator() throws IOException {
		Resource[] resources = new Resource[this.xsdResources.length];
		for(int i = this.xsdResources.length - 1 ; i >= 0 ; i--){
			resources[i] = this.xsdResources[i];
		} 
		return XmlValidatorFactory.createValidator(resources , "http://www.w3.org/2001/XMLSchema"); // "resources"={[Inline schema for class path resource [wsdl/ibsclient/QueryAcctDateService.wsdl]]}
	}
	
	private void inlineIncludes(XmlSchema schema , Set<XmlSchema> processedIncludes , Set<XmlSchema> processedImports) {
		processedIncludes.add(schema);
		
		XmlSchemaObjectCollection xmlSchemaObjectCollection1 = schema.getIncludes();
		for(int i=0 ; i < xmlSchemaObjectCollection1.getCount() ; ++i){
			XmlSchemaExternal xmlSchemaExternal = (XmlSchemaExternal) xmlSchemaObjectCollection1.getItem(i); //"xmlSchemaExternal"=XmlSchemaImport,schemaLocation="QueryAcctDateService.xsd"
			if(xmlSchemaExternal instanceof XmlSchemaInclude){// 此分支为false
				XmlSchema xmlSchema = xmlSchemaExternal.getSchema();
				XmlSchemaObjectCollection xmlSchemaObjectCollection2 = xmlSchema.getItems();
				if(!processedIncludes.contains(xmlSchema)){
					inlineIncludes(xmlSchema , processedIncludes , processedImports);
					findImports(xmlSchema , processedImports , processedIncludes);
					XmlSchemaObjectCollection xmlSchemaObjectCollection3 = xmlSchema.getItems();
					for(int j = 0 ; j < xmlSchemaObjectCollection3.getCount() ; j++){
						XmlSchemaObject xmlSchemaObject = xmlSchemaObjectCollection3.getItem(j);
						xmlSchemaObjectCollection2.add(xmlSchemaObject);
					}
				}
				
				xmlSchemaObjectCollection2.remove(xmlSchemaExternal);
			}
			
		}
	}
	
	private void findImports(XmlSchema schema , Set<XmlSchema> processedImports , Set<XmlSchema> processedIncludes) {
		
		processedImports.add(schema);
		
		XmlSchemaObjectCollection xmlSchemaObjectCollection = schema.getIncludes();
		for(int i = 0 ; i < xmlSchemaObjectCollection.getCount() ; i++){
			XmlSchemaExternal xmlSchemaExternal = (XmlSchemaExternal) xmlSchemaObjectCollection.getItem(i); //"xmlSchemaExternal"=XmlSchemaImport,schemaLocation="QueryAcctDateService.xsd"
			if(xmlSchemaExternal instanceof XmlSchemaImport){
				XmlSchemaImport xmlSchemaImport = (XmlSchemaImport) xmlSchemaExternal ;
				XmlSchema xmlSchema = xmlSchemaImport.getSchema();
				if((!"http://www.w3.org/XML/1998/namespace".equals(xmlSchemaImport.getNamespace())) && (xmlSchema!=null) && (!processedImports.contains(xmlSchema))){
					this.xmlSchemas.add(xmlSchema);
					inlineIncludes(xmlSchema , processedIncludes , processedImports);
					findImports(xmlSchema , processedImports , processedIncludes);
				}
				
				xmlSchemaExternal.setSchemaLocation(null);
			}
		}
		
	}
	
	public String toString(){
		StringBuilder builder = new StringBuilder("CommonsXsdSchemaCollection");
		builder.append('{');
		for(int i = 0 ; i < xmlSchemas.size() ; i++){
			XmlSchema schema = xmlSchemas.get(i);
			builder.append(schema.getTargetNamespace());
			if(i < xmlSchemas.size() - 1){
				builder.append(',');
			}
		}
		builder.append('}');
		return builder.toString() ;
	}
	
	private class ClasspathUriResolver extends DefaultURIResolver {
		// http://gsp.service.bankcomm.com/QueryAcctDateService  , QueryAcctDateService.xsd  ,  file:/E:/spring/weather/WebRoot/WEB-INF/classes/wsdl/ibsclient/QueryAcctDateService.wsdl
		public InputSource resolveEntity(String namespace , String schemaLocation , String baseUri) {
			// http://gsp.bankcomm.com  ,  headerOfResponse.xsd  ,  file:/E:/spring/weather/WebRoot/WEB-INF/classes/wsdl/ibsclient/QueryAcctDateService.xsd
			if(resourceLoader != null){
				Resource resource = resourceLoader.getResource(schemaLocation); // class path resource [QueryAcctDateService.xsd]
				if(resource.exists()){ // false
					return createInputSource(resource);
				}
				else if(StringUtils.hasLength(baseUri)){
					try {
						Resource baseUriResource = new UrlResource(baseUri);
						resource = baseUriResource.createRelative(schemaLocation);
						if(resource.exists()){
							return createInputSource(resource);
						}
					} catch (IOException e) {
						
					}
				}
				
				String classpathLocation = "classpath:/" + schemaLocation ;
				resource = resourceLoader.getResource(classpathLocation);
				if(resource.exists()){
					return createInputSource(resource);
				}
			}
			
			return super.resolveEntity(namespace, schemaLocation , baseUri);
		}
		
		private InputSource createInputSource(Resource resource) {
			try {
				return SaxUtils.createInputSource(resource);
			} catch (IOException e) {
				throw new CommonsXsdSchemaException("Could not resolve location" , e);
			}
		}
		
		
	}

}
